package pers.jwt.util;

import io.jsonwebtoken.*;
import org.bouncycastle.util.encoders.Base64;
import pers.jwt.exception.MyJWTException;
import pers.myutil.local.encryption.MD5;
import pers.myutil.local.random.UUID;

import javax.crypto.SecretKey;
import javax.crypto.spec.SecretKeySpec;
import javax.servlet.http.HttpServletResponse;
import java.security.NoSuchAlgorithmException;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;

/**
 * JWT公共类
 */
public class JWTUtil {

    /**
     * 添加hesder信息
     * @param response 返回
     * @param jwt json web token
     * @param parameterName 参数名
     */
    public static void addHesder(HttpServletResponse response,String jwt,String  parameterName){
        response.setHeader("Access-Control-Expose-Headers",parameterName);
        response.addHeader(parameterName, jwt);
    }

    /**
     * 签发JWT
     * @param ttlMillis 过期时间 单位毫秒
     * @param claimsMap 私有声明
     * @param key 秘钥
     * @param subject 签发人
     * @param signatureAlgorithm 制定签名算法
     * @return token
     */
    public static String createJWT(long ttlMillis, Map<String,Object> claimsMap,String key,String subject,SignatureAlgorithm signatureAlgorithm)  {

        // 生成JWT的时间
        long nowMillis = System.currentTimeMillis();
        Date now = new Date(nowMillis);

        // 设置jwt的body
        JwtBuilder builder = Jwts.builder()
                //如果有私有声明，一定要先设置这个自己创建的私有的声明，这个是给builder的claim赋值，一旦写在标准的声明赋值之后，就是覆盖了那些标准的声明的
                .setClaims(claimsMap)
                //设置jti(JWT ID)：是JWT的唯一标识，根据业务需要，这个可以设置为一个不重复的值，主要用来作为一次性token,从而回避重放攻击。
                .setId(UUID.getUUID())
                //iat: jwt的签发时间
                .setIssuedAt(now)
                //代表这个JWT的主体，即它的所有人，这个是一个json格式的字符串，可以存放什么userid，roldid之类的，作为什么用户的唯一标志。
                .setSubject(subject)
                //设置签名使用的签名算法和签名使用的秘钥
                .signWith(signatureAlgorithm,generalKey(key,0,"AES"));
        if(ttlMillis >= 0){
            long expMillis = nowMillis+ttlMillis;
            Date exp = new Date(expMillis);
            // 设置过期时间
            builder.setExpiration(exp);
        }
        return builder.compact();
    }

//    /**
//     * 签发JWT
//     * @param ttlMillis 过期时间
//     * @param claims 声明
//     * @param key 秘钥
//     * @param subject 签发人
//     * @param signatureAlgorithm 制定签名算法
//     * @return
//     */
//    public static String createJWT(long ttlMillis,Claims claims,String key,String subject,SignatureAlgorithm signatureAlgorithm) throws NoSuchAlgorithmException {
//
//        // 生成JWT的时间
//        long nowMillis = System.currentTimeMillis();
//        Date now = new Date(nowMillis);
//
//        // 设置jwt的body
//        JwtBuilder builder = Jwts.builder()
//                //如果有私有声明，一定要先设置这个自己创建的私有的声明，这个是给builder的claim赋值，一旦写在标准的声明赋值之后，就是覆盖了那些标准的声明的
//                .setClaims(claims)
//                //设置jti(JWT ID)：是JWT的唯一标识，根据业务需要，这个可以设置为一个不重复的值，主要用来作为一次性token,从而回避重放攻击。
//                .setId(UUID.getUUID())
//                //iat: jwt的签发时间
//                .setIssuedAt(now)
//                //代表这个JWT的主体，即它的所有人，这个是一个json格式的字符串，可以存放什么userid，roldid之类的，作为什么用户的唯一标志。
//                .setSubject(subject)
//                //设置签名使用的签名算法和签名使用的秘钥
//                .signWith(signatureAlgorithm,generalKey(key,0,"AES"));
//        if(ttlMillis >= 0){
//            long expMillis = nowMillis+ttlMillis;
//            Date exp = new Date(expMillis);
//            // 设置过期时间
//            builder.setExpiration(exp);
//        }
//        return builder.compact();
//    }


    /**
     * 获取秘钥
     * @param key 秘钥
     * @param offset 偏移量
     * @param algorithm  加密算法
     * @return 秘钥
     * @throws NoSuchAlgorithmException
     */
    private static SecretKey generalKey(String key,int offset,String algorithm) {
        byte[] encodedKey = Base64.decode(MD5.getMd5(key));
        return new SecretKeySpec(encodedKey, 0, encodedKey.length, algorithm);
    }

    /**
     * Token解密
     * @param token 用户Token
     * @param key 秘钥
     * @return 解密结果
     */
    public static Claims parseJWT(String token, String key) {
        //得到DefaultJwtParser
        return Jwts.parser()
                .setSigningKey(generalKey(key,0,"AES"))
                .parseClaimsJws(token).getBody();
    }

    /**
     * 验证token
     * @param token token
     * @param key 秘钥
     * @throws MyJWTException
     */
    public static void validateJWT(String token,String key) throws MyJWTException {
        try {
            Claims claims = parseJWT(token,key);
        }catch (ExpiredJwtException e){
            throw new MyJWTException("Token已经失效！");
        } catch (Exception e){
            e.printStackTrace();
            throw new MyJWTException("验证不通过！");
        }

    }

    public static void main(String[] args) throws InterruptedException, MyJWTException {
//        Map<String,Object> map = new HashMap<>();
//        map.put("id","1");
//        map.put("name","我去");
//        String a = JWTUtil.createJWT(3000,map,"2","我去",SignatureAlgorithm.HS256);
//        System.out.println(a);
//        System.out.println(JWTUtil.parseJWT(a,"2").get("id"));
//        Thread.sleep(1000);
//        System.out.println(JWTUtil.parseJWT(a,"2").get("id"));
//        JWTUtil.validateJWT(a,"2");
//        Thread.sleep(2000);
//        JWTUtil.validateJWT("2.2.2","2");
//        Map map = new HashMap();
//        map.put("user",1);
//        System.out.println(JWTUtil.createJWT(7200000,map,"123456789","书生长卷",SignatureAlgorithm.HS256));
        Claims a = JWTUtil.parseJWT("eyJhbGciOiJIUzI1NiJ9.eyJzdWIiOiLkuabnlJ_plb_ljbciLCJleHAiOjE1NTEwOTE4NjksInVzZXIiOjEsImlhdCI6MTU1MTA4NDY2OSwianRpIjoiMTY0ZThlOTgxNzlmNDY4YWJkMjM2NmNkNTRkMWQ0MjUifQ.v5bjTFb9LzoHA2_hQxF7QFjAF9cbF8MxchwxN_zI1XE","123456789");
        System.out.println(a.getSubject());
    }

}
